package com.franklin.ideaplugin.maphelper.utils;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
import com.franklin.ideaplugin.api.beans.declaration.IDeclaration;
import com.franklin.ideaplugin.api.beans.variable.IVariable;
import com.franklin.ideaplugin.api.utils.psi.PsiClassUtils;
import com.franklin.ideaplugin.api.utils.psi.PsiDeclarationUtils;
import com.franklin.ideaplugin.maphelper.beans.colmember.IColMember;
import com.franklin.ideaplugin.maphelper.beans.colmember.PsiColMember;
import com.franklin.ideaplugin.maphelper.beans.colmember.StringColMember;
import com.franklin.ideaplugin.maphelper.col.declaration.ColDeclarationFactory;
import com.franklin.ideaplugin.maphelper.col.declaration.IColDeclaration;
import com.franklin.ideaplugin.maphelper.col.method.IColCreateMethod;
import com.franklin.ideaplugin.maphelper.col.method.IColInsertMethod;
import com.franklin.ideaplugin.maphelper.col.method.IColMethod;
import com.franklin.ideaplugin.maphelper.col.method.IColQueryMethod;
import com.franklin.ideaplugin.maphelper.col.operation.*;
import com.franklin.ideaplugin.maphelper.resolver.AbstractColMemberResolverFactory;
import com.google.common.collect.Lists;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.function.Function;

/**
 * @author Ye Junhui
 * @since 2024/3/6
 */
public class PsiColUtils {

    /**
     * 寻找集合写入方法
     *
     * @param psiMethod
     * @return
     */
    public static IColInsertMethod findColInsertMethod(PsiMethod psiMethod, Set<IColInsertMethod> colInsertMethods) {
        //方法名
        String methodName = psiMethod.getName();
        //参数
        int parametersCount = psiMethod.getParameterList().getParametersCount();
        for (IColInsertMethod colInsertMethod : colInsertMethods) {
            //方法名
            if (colInsertMethod.getMethodName().equals(methodName)) {
                //参数数量
                if (parametersCount == colInsertMethod.getParamCount()) {
                    return colInsertMethod;
                }
            }
        }
        return null;
    }

    /**
     * 寻找集合创建方法
     *
     * @param psiMethod
     * @param colCreateMethods
     * @return
     */
    public static IColCreateMethod findColCreateMethod(PsiMethod psiMethod, Set<IColCreateMethod> colCreateMethods) {
        //方法名
        String methodName = psiMethod.getName();
        //参数
        PsiParameterList parameterList = psiMethod.getParameterList();
        int parametersCount = parameterList.getParametersCount();
        for (IColCreateMethod colCreateMethod : colCreateMethods) {
            //方法名
            if (colCreateMethod.getMethodName().equals(methodName)) {
                //可变参数
                if (colCreateMethod.getParamCount() == -1) {
                    return colCreateMethod;
                }
                //参数数量
                if (parametersCount == colCreateMethod.getParamCount()) {
                    PsiParameter parameter = parameterList.getParameter(0);
                    if (Objects.nonNull(parameter) && !parameter.isVarArgs()) {
                        return colCreateMethod;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 寻找集合查询方法
     *
     * @param psiMethod
     * @return
     */
    public static IColQueryMethod findColQueryMethod(PsiMethod psiMethod, Set<IColQueryMethod> colQueryMethods) {
        //方法名
        String methodName = psiMethod.getName();
        //参数
        int parametersCount = psiMethod.getParameterList().getParametersCount();
        for (IColQueryMethod colQueryMethod : colQueryMethods) {
            //方法名
            if (colQueryMethod.match(methodName)) {
                return colQueryMethod;
            }
        }
        return null;
    }

    /**
     * 从代码块中寻找map成员
     *
     * @param colVariable
     * @param psiCodeBlock
     * @param colOperationList
     * @param colMemberResolverFactory
     * @return
     */
    public static List<IColMember> findColMemberInCodeBlock(
            IVariable colVariable,
            @Nullable PsiCodeBlock psiCodeBlock,
            List<IColReadWriteOperation> colOperationList,
            AbstractColMemberResolverFactory colMemberResolverFactory
    ) {
        if (Objects.isNull(psiCodeBlock) || Objects.isNull(colVariable)) {
            return Lists.newArrayList();
        }
        //结果
        List<IColMember> result = Lists.newArrayList();
        //寻找put方法调用
        Collection<PsiCodeBlock> psiCodeBlocks = PsiTreeUtil.collectElementsOfType(psiCodeBlock, PsiCodeBlock.class);
        psiCodeBlocks.stream()
                .map(PsiCodeBlock::getStatements)
                .flatMap(Arrays::stream)
                .filter(psiStatement -> psiStatement instanceof PsiExpressionStatement)
                .map(psiStatement -> (PsiExpressionStatement) psiStatement)
                .forEach(psiExpressionStatement -> {
                    Collection<PsiMethodCallExpression> psiMethodCallExpressions = PsiTreeUtil.collectElementsOfType(psiExpressionStatement, PsiMethodCallExpression.class);
                    psiMethodCallExpressions.forEach(psiMethodCallExpression -> {
                        //方法调用
                        PsiReferenceExpression psiReferenceExpression = psiMethodCallExpression.getMethodExpression();
                        //调用者匹配
                        PsiIdentifier callerExpression = PsiTreeUtil.findChildOfType(psiReferenceExpression, PsiIdentifier.class);
                        if (Objects.isNull(callerExpression)) {
                            return;
                        }
                        if (!callerExpression.getText().equals(colVariable.getName())) {
                            return;
                        }

                        //方法对象
                        PsiMethod psiMethod = psiMethodCallExpression.resolveMethod();
                        if (Objects.isNull(psiMethod)) {
                            return;
                        }

                        //对象类型
                        PsiType psiType = colVariable.getPsiType();
                        PsiClassType psiClassType = (PsiClassType) psiType;
                        PsiClass psiClass = psiClassType.resolve();

                        //操作
                        for (IColReadWriteOperation colOperation : colOperationList) {
                            //同父类
                            PsiClass colClass = PsiClassUtils.findPsiClass(psiClass.getProject(), colOperation.getClassQualifiedName());
                            if (PsiClassUtils.isInheritorOrEquals(colClass, psiClass)) {
                                //插入全部方法
                                Set<IColInsertMethod> insertAllMethods = colOperation.getInsertAllMethods();
                                IColInsertMethod colInsertAllMethod = findColInsertMethod(psiMethod, insertAllMethods);
                                if (Objects.nonNull(colInsertAllMethod)) {
                                    //寻找声明
                                    //寻找表达式
                                    PsiMethodCallExpression valueMethodCallExpression = PsiTreeUtil.findChildOfType(psiMethodCallExpression, PsiMethodCallExpression.class);
                                    IColDeclaration mapDeclaration = findColExpressionDeclaration(valueMethodCallExpression);
                                    //写入全部
                                    Collection<IColMember> insertAllMembers = colMemberResolverFactory.resolveExpression(valueMethodCallExpression, mapDeclaration);
                                    CollectionUtil.addAll(result, insertAllMembers);
                                    return;
                                }

                                //插入方法
                                Set<IColInsertMethod> insertMethods = colOperation.getInsertMethods();
                                IColInsertMethod colInsertMethod = findColInsertMethod(psiMethod, insertMethods);
                                if (Objects.nonNull(colInsertMethod)) {
                                    //解析写入参数
                                    //参数列表
                                    PsiExpressionList argumentList = psiMethodCallExpression.getArgumentList();
                                    PsiExpression[] psiExpressions = PsiTreeUtil.getChildrenOfType(argumentList, PsiExpression.class);
                                    if (ArrayUtil.isEmpty(psiExpressions)) {
                                        return;
                                    }

                                    //封装数据
                                    PsiColMember psiColMember = new PsiColMember();
                                    psiColMember.setPsiExpressions(psiExpressions);
                                    psiColMember.setColInsertMethod(colInsertMethod);
                                    result.add(psiColMember);
                                }

                                //查询方法
                                Set<IColQueryMethod> queryMethods = colOperation.getQueryMethods();
                                IColQueryMethod colQueryMethod = findColQueryMethod(psiMethod, queryMethods);
                                if (Objects.nonNull(colQueryMethod)){
                                    //解析查询参数
                                    //参数列表
                                    PsiExpressionList argumentList = psiMethodCallExpression.getArgumentList();
                                    PsiExpression[] psiExpressions = PsiTreeUtil.getChildrenOfType(argumentList, PsiExpression.class);
                                    if (ArrayUtil.isEmpty(psiExpressions)) {
                                        return;
                                    }

                                    //key
                                    PsiExpression keyExpression = psiExpressions[colQueryMethod.getQueryTargetIndex()];
                                    String keyText = keyExpression.getText();
                                    PsiType keyType = keyExpression.getType();
                                    String typeText = keyType.getCanonicalText();

                                    //封装数据
                                    StringColMember stringColMember = new StringColMember();
                                    stringColMember.setKeyText(keyText);
                                    stringColMember.setValueClass(typeText);
                                    stringColMember.setValueText("");
                                    stringColMember.setValue("");
                                    result.add(stringColMember);
                                }
                            }
                        }
                    });
                });

        return result;
    }

    /**
     * 寻找集合定义
     *
     * @param psiExpression
     * @return
     */
    @Nullable
    public static IColDeclaration findColExpressionDeclaration(PsiElement psiExpression) {
        IDeclaration declaration = PsiDeclarationUtils.findExpressionDeclaration(psiExpression);
        if (Objects.isNull(declaration)) {
            return null;
        }
        return ColDeclarationFactory.getInstance().create(declaration);
    }

    /**
     * 获取创建操作
     *
     * @param colDeclaration
     * @return
     */
    public static IColCreateMethod findColCreateMethod(IColDeclaration colDeclaration, Collection<IColCreateOperation> colCreateOperations) {
        return findColMethod(
                colDeclaration,
                colCreateOperations,
                IColCreateOperation::getCreateMethods
        );
    }

    /**
     * 获取查询操作
     *
     * @param colDeclaration
     * @return
     */
    public static IColQueryMethod findColQueryMethod(IColDeclaration colDeclaration, Collection<IColReadWriteOperation> colReadWriteOperations) {
        return findColMethod(
                colDeclaration,
                colReadWriteOperations,
                IColReadWriteOperation::getQueryMethods
        );
    }

    /**
     * 获取查询操作
     *
     * @param psiMethodCallExpression
     * @return
     */
    public static IColQueryMethod findColQueryMethod(PsiMethodCallExpression psiMethodCallExpression, Collection<IColReadWriteOperation> colReadWriteOperations) {
        return findColMethod(
                psiMethodCallExpression,
                colReadWriteOperations,
                IColReadWriteOperation::getQueryMethods
        );
    }

    /**
     * 获取集合操作方法
     *
     * @param colDeclaration
     * @param colOperations
     * @return
     */
    public static <O extends IColOperation, M extends IColMethod> M findColMethod(
            IColDeclaration colDeclaration,
            Collection<O> colOperations,
            Function<O, Set<M>> methodMapper
    ) {
        IDeclaration declaration = colDeclaration.getDeclaration();
        PsiMethodCallExpression psiMethodCallExpression = declaration.getCreateExpression(PsiMethodCallExpression.class);
        return findColMethod(
                psiMethodCallExpression,
                colOperations,
                methodMapper
        );
    }

    /**
     * 获取集合操作方法
     *
     * @param psiMethodCallExpression
     * @param colOperations
     * @param methodMapper
     * @param <O>
     * @param <M>
     * @return
     */
    public static <O extends IColOperation, M extends IColMethod> M findColMethod(
            PsiMethodCallExpression psiMethodCallExpression,
            Collection<O> colOperations,
            Function<O, Set<M>> methodMapper
    ) {
        //方法
        PsiMethod psiMethod = psiMethodCallExpression.resolveMethod();
        if (Objects.isNull(psiMethod)) {
            return null;
        }

        //寻找创建方法
        Project project = psiMethod.getProject();
        PsiClass containingClass = psiMethod.getContainingClass();
        for (O colOperation : colOperations) {
            //比较类
            PsiClass psiClass = PsiClassUtils.findPsiClass(project, colOperation.getClassQualifiedName());
            if (!PsiClassUtils.isInheritorOrEquals(containingClass, psiClass)) {
                continue;
            }
            //比较方法
            Set<M> colMethods = methodMapper.apply(colOperation);
            M colMethod = findColMethod(psiMethod, colMethods);
            if (Objects.nonNull(colMethod)) {
                return colMethod;
            }
        }

        return null;
    }

    /**
     * 寻找集合创建方法
     *
     * @param psiMethod
     * @param colMethods
     * @return
     */
    public static <M extends IColMethod> M findColMethod(PsiMethod psiMethod, Set<M> colMethods) {
        M method = colMethods.iterator().next();
        if (method instanceof IColCreateMethod) {
            return (M) findColCreateMethod(psiMethod, (Set<IColCreateMethod>) colMethods);
        } else if (method instanceof IColInsertMethod) {
            return (M) findColInsertMethod(psiMethod, (Set<IColInsertMethod>) colMethods);
        } else if (method instanceof IColQueryMethod) {
            return (M) findColQueryMethod(psiMethod, (Set<IColQueryMethod>) colMethods);
        }
        return null;
    }
}
